class Schedule < ActiveRecord::Base
#  belongs_to :user
  has_many :scheduled_items, :dependent=>:destroy
  has_many :events, :through=>:scheduled_items
  belongs_to :user

  # TODO: Called in an INSTANCE of schedule so there is a list of events...
  # build should algorithmically CLOBBER @events with a new list of events
  def build
    lectures = self.events.select {|x| x.type == 'Lecture'}
    self.events = disLabWithMinConflict(lectures, lectures)[1]
  end

  def add_event(event)
    self.findAndRemoveSimilar(event)
    self.events << event
  end

  def findAndRemoveSimilar(event)
    toRemove = self.events.select { |e|
      e.course == event.course and e.event_type == event.event_type and e != event
    }
    toRemove.each do |evt| self.events.delete(evt) end
    return toRemove
  end

  def title()
    timeStr = self.created_at.strftime("%Y-%M-%d %I:%M%p")
    eventsWithCourse = self.events.select {|x| x.course }
    if eventsWithCourse.length > 0:
      return eventsWithCourse[0].course.term.name+" ("+timeStr+")"
    end
    return "("+timeStr+")"
  end

  private

  # Pick discussions and labs that minimize conflicts
  # It recursively tries all combinations of discussions and labs to find the optimal solution
  def disLabWithMinConflict(lectures, existingEvents, type = :DIS)
    # Done when there is no lecture
    return [0, existingEvents] if lectures.size == 0

    # Each lecture is visited twice, one for discussion and another for labs
    # Once lab is chosen, move on to next lecture
    # i.e. this method can be thought of as two mutually recursive functions,
    # one looking for an optimal discussion and another looking for an optimal lab
    # but we accomplish this by using type flag that alternates between :DIS and :LAB
    if type == :DIS
      candidates = lectures[0].course.discussions
      nextLectures = lectures
      nextType = :LAB
    else # type = :LAB
      candidates = lectures[0].course.labs
      nextLectures = lectures[1..(lectures.size-1)]
      nextType = :DIS
    end

    # When there is no candidates (discussions/labs), move on.
    return disLabWithMinConflict(nextLectures, existingEvents, nextType) if candidates.size <= 0

    for currentCandidate in candidates:
      # Compute the minimum conflicting minutes using this candidate
      # Then add the conflicting minutes of the current candidate with the existing events
      conflictsAndEvents = disLabWithMinConflict(nextLectures, existingEvents + [currentCandidate], nextType)
      conflictsAndEvents[0] += existingEvents.inject(0) { |sum, event| sum + currentCandidate.conflicted_minutes_with(event) }

      # If this candidate did better, update the optimal solution
      optimalSolution = conflictsAndEvents if not optimalSolution or conflictsAndEvents[0] < optimalSolution[0]
    end

    return optimalSolution
  end
end
